/* machine_sdcard.c */

#include "machine_sdcard.h"

#include "sdcard_sdio.h"
#include "board_init.h"

#include <stdio.h>

//#include "vfs.h"

sdcard_t hw_sdcard;

static bool b_machine_sdcard_is_initialised = false;

bool machine_sdcard_init(void)
{
    //ignore
    
    //printf("machine_sdcard_init()\r\n");

    if (0u != sdcard_init(&hw_sdcard, BOARD_SDCARD_SDIO_PORT) )
    {
        return false;
    }

    //printf("block_num: %lu\r\n", hw_sdcard.block_cnt );

    b_machine_sdcard_is_initialised = true;

    return true;
}

uint32_t machine_sdcard_get_block_size(void)
{
    //printf("machine_sdcard_get_block_size()\r\n");
    //return machine_sdcard_struct.block_len;
    return SDCARD_BLOCK_SIZE;
}

uint32_t machine_sdcard_get_block_count(void)
{
    //printf("machine_sdcard_get_block_count\r\n");
    return hw_sdcard.block_cnt;
}

void machine_sdcard_flush(void)
{
    //printf("machine_sdcard_flush\r\n");
    //nothing to do
}

/*
 * dest : buffer ptr to save read data
 * block_num : the start number of block to read ,scope :[0 ~ 1023]
 */
bool machine_sdcard_read_block(uint8_t *dest, uint32_t block_num)
{
    //printf("machine_sdcard_read_block(), %lu\r\n", block_num);

    sdcard_read_single_block(&hw_sdcard, block_num, dest);

    //ignore
    return true;
}

/*
 * dest : buffer ptr to save read data
 * block_num : the start number of block to read ,scope :[0 ~ 1023]
 * num_blocks : how many blocks to read
 */
mp_uint_t machine_sdcard_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
    uint32_t i = 0;
    mp_uint_t ret = 0;
    
    for (i = 0; i < num_blocks; i++)
    {
        if (!machine_sdcard_read_block(dest + i * SDCARD_BLOCK_SIZE, block_num + i))
        {
            ret = 1; // error
            break;
        }
    }
    
    return ret; // success
}

/*
 * src : buffer ptr to program data
 * block_num : the start number of block to write ,scope :[0 ~ 1023]
 */
bool machine_sdcard_write_block(const uint8_t *src, uint32_t block_num)
{
    //printf("machine_sdcard_write_block() %lu\r\n", block_num);

    sdcard_write_single_block(&hw_sdcard, block_num, (uint8_t *)src);

    //ignore
    return true;
}

/*
 * src : buffer ptr to program data
 * block_num : the start number of block to write ,scope :[0 ~ 1023]
 * num_blocks : how many blocks to write
 */
mp_uint_t machine_sdcard_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks)
{

    for (size_t i = 0; i < num_blocks; i++)
    {
        if (!machine_sdcard_write_block(src + i * SDCARD_BLOCK_SIZE, block_num + i))
        {
            return 1; // error
        }
    }
    return 0; // success
}


/******************************************************************************/
// MicroPython bindings
// Expose the SD card or MMC as an object with the block protocol.

const mp_obj_base_t machine_sdcard_obj = { &machine_sdcard_type };

STATIC mp_obj_t machine_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args)
{
    // check arguments
    mp_arg_check_num(n_args, n_kw, 0, 0, false);

    // return singleton object
    return MP_OBJ_FROM_PTR(&machine_sdcard_obj);
}

STATIC mp_obj_t machine_sdcard_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf)
{
    mp_buffer_info_t bufinfo;
    mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE);
    mp_uint_t ret = machine_sdcard_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / SDCARD_BLOCK_SIZE);
    return MP_OBJ_NEW_SMALL_INT(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_sdcard_readblocks_obj, machine_sdcard_readblocks);

STATIC mp_obj_t machine_sdcard_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) {
    mp_buffer_info_t bufinfo;
    mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
    mp_uint_t ret = machine_sdcard_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / SDCARD_BLOCK_SIZE);
    return MP_OBJ_NEW_SMALL_INT(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_sdcard_writeblocks_obj, machine_sdcard_writeblocks);

STATIC mp_obj_t machine_sdcard_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) {
    mp_int_t cmd = mp_obj_get_int(cmd_in);
    switch (cmd) {
        case MP_BLOCKDEV_IOCTL_INIT: 
            machine_sdcard_init();
            if(b_machine_sdcard_is_initialised)
            {
                return MP_OBJ_NEW_SMALL_INT(0);
            }
            return MP_OBJ_NEW_SMALL_INT(1);
        
        case MP_BLOCKDEV_IOCTL_DEINIT: 
            machine_sdcard_flush(); 
            return MP_OBJ_NEW_SMALL_INT(0); // TODO properly
        
        case MP_BLOCKDEV_IOCTL_SYNC: 
            machine_sdcard_flush(); 
            return MP_OBJ_NEW_SMALL_INT(0);
        
        case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: 
            return MP_OBJ_NEW_SMALL_INT(machine_sdcard_get_block_count());
            
        case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: 
            return MP_OBJ_NEW_SMALL_INT(machine_sdcard_get_block_size());
            
        default: 
            return mp_const_none;
    }
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_sdcard_ioctl_obj, machine_sdcard_ioctl);

STATIC const mp_rom_map_elem_t machine_sdcard_locals_dict_table[] = {
    { MP_ROM_QSTR(MP_QSTR_readblocks),      MP_ROM_PTR(&machine_sdcard_readblocks_obj) },
    { MP_ROM_QSTR(MP_QSTR_writeblocks),     MP_ROM_PTR(&machine_sdcard_writeblocks_obj) },
    { MP_ROM_QSTR(MP_QSTR_ioctl),           MP_ROM_PTR(&machine_sdcard_ioctl_obj) },
};

STATIC MP_DEFINE_CONST_DICT(machine_sdcard_locals_dict, machine_sdcard_locals_dict_table);

const mp_obj_type_t machine_sdcard_type = {
    { &mp_type_type },
    .name           = MP_QSTR_SDCard,
    .make_new       = machine_sdcard_make_new,
    .locals_dict    = (mp_obj_dict_t*)&machine_sdcard_locals_dict,
};

//vfs init func
void machine_sdcard_init_vfs(fs_user_mount_t *vfs)
{
    vfs->base.type = &mp_fat_vfs_type;
    vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_NATIVE | MP_BLOCKDEV_FLAG_HAVE_IOCTL;
    vfs->fatfs.drv = vfs;
    vfs->blockdev.readblocks[0]  = MP_OBJ_FROM_PTR(&machine_sdcard_readblocks_obj);
    vfs->blockdev.readblocks[1]  = MP_OBJ_FROM_PTR(&machine_sdcard_obj);
    vfs->blockdev.readblocks[2]  = MP_OBJ_FROM_PTR(machine_sdcard_read_blocks); // native version
    vfs->blockdev.writeblocks[0] = MP_OBJ_FROM_PTR(&machine_sdcard_writeblocks_obj);
    vfs->blockdev.writeblocks[1] = MP_OBJ_FROM_PTR(&machine_sdcard_obj);
    vfs->blockdev.writeblocks[2] = MP_OBJ_FROM_PTR(machine_sdcard_write_blocks); // native version
    vfs->blockdev.u.ioctl[0]     = MP_OBJ_FROM_PTR(&machine_sdcard_ioctl_obj);
    vfs->blockdev.u.ioctl[1]     = MP_OBJ_FROM_PTR(&machine_sdcard_obj);
}

/* EOF. */

